package cn.com.guage.transtation;

import com.mysql.cj.util.StringUtils;

import java.sql.*;
import java.util.*;

import java.sql.*;
import java.util.*;

public class DistributedTransaction3PC {

    static class DBConfig {
        String dbName;
        String url;
        String user;
        String password;

        DBConfig(String dbName,String url, String user, String password) {
            this.dbName = dbName;
            this.url = url;
            this.user = user;
            this.password = password;
        }
    }

    static final DBConfig coordinatorConfig = new DBConfig("coordinator_db",
            "jdbc:mysql://localhost:3306/coordinator_db", "root", "123456");

    static final DBConfig bankAConfig = new DBConfig("db1",
            "jdbc:mysql://localhost:3306/3pc-db1", "root", "123456");

    static final DBConfig bankBConfig = new DBConfig("db2",
            "jdbc:mysql://localhost:3306/3pc-db2", "root", "123456");

    public static void main(String[] args) {
        String xid = UUID.randomUUID().toString();
        List<DBConfig> participants = Arrays.asList(bankAConfig, bankBConfig);

        try {
           Boolean canCommit =  canCommitPhase(xid, participants);
            if (canCommit) {
                Boolean preCommit = preCommitPhase(xid, participants);
                if (preCommit) {
                    doCommitPhase(xid, participants);
                } else {
                    doAbort(xid, participants);
                }
            } else {
                doAbort(xid, participants);
            }
        } catch (SQLException e) {
            e.printStackTrace();
            doAbort(xid, participants);
        }
    }

    // ========== 阶段1: CanCommit ==========
    private static boolean canCommitPhase(String xid, List<DBConfig> participants) throws SQLException {
        try (Connection coordConn = getConnection(coordinatorConfig)) {
            coordConn.setAutoCommit(false);  // 关闭自动提交

            // 记录事务到协调表
            PreparedStatement ps = coordConn.prepareStatement(
                    "INSERT INTO transaction_coordinator (xid, phase, participants) VALUES (?, 'CAN_COMMIT', ?)");
            ps.setString(1, xid);
            ps.setString(2, participants.toString());
            ps.executeUpdate();

            // 询问所有参与者
            for (DBConfig participant : participants) {
                try (Connection conn = getConnection(participant)) {
                    conn.setAutoCommit(false);  // 参与者关闭自动提交
                    if (participant.dbName=="db1") {
                        Boolean participantCanCommit =  zhangSanCanCommit(conn, xid);
                        if (!participantCanCommit) {
                            coordConn.rollback();
                            return false;
                        }
                    }
                    if (participant.dbName=="db2") {
                        Boolean participantCanCommit =  lisiCanCommit(conn, xid);
                        if (!participantCanCommit) {
                            coordConn.rollback();
                            return false;
                        }
                    }

                }
            }
            coordConn.commit();
            return true;
        }
    }

    // ========== 阶段2: PreCommit ==========
    private static boolean preCommitPhase(String xid, List<DBConfig> participants) throws SQLException {
        try (Connection coordConn = getConnection(coordinatorConfig)) {
            coordConn.setAutoCommit(false);

            updateCoordinatorPhase(coordConn, xid, "PRE_COMMIT");

            for (DBConfig participant : participants) {
                try (Connection conn = getConnection(participant)) {
                    conn.setAutoCommit(false);  // 关闭自动提交
                    if (!participantPreCommit(conn, xid)) {
                        coordConn.rollback();
                        return false;
                    }
                }
            }
            coordConn.commit();
            return true;
        }
    }

    // ========== 阶段3: DoCommit ==========
    private static void doCommitPhase(String xid, List<DBConfig> participants) throws SQLException {
        try (Connection coordConn = getConnection(coordinatorConfig)) {
            coordConn.setAutoCommit(false);
            updateCoordinatorPhase(coordConn, xid, "COMMIT");

            // 获取业务连接（必须关闭自动提交）
            try (Connection connA = getConnection(bankAConfig);
                 Connection connB = getConnection(bankBConfig)) {

                connA.setAutoCommit(false);
                connB.setAutoCommit(false);

                // 执行转账操作
                PreparedStatement psA = connA.prepareStatement(
                        "UPDATE account SET balance = balance - 1000 WHERE name = '张三'");
                psA.executeUpdate();

                PreparedStatement psB = connB.prepareStatement(
                        "UPDATE account SET balance = balance + 1000 WHERE name = '李四'");
                psB.executeUpdate();

                // 提交事务
                connA.commit();
                connB.commit();
                coordConn.commit();
                System.out.println("事务提交成功！");
            }
        }
    }

    // ========== 工具方法 ==========
    private static Connection getConnection(DBConfig config) throws SQLException {
        Connection conn = DriverManager.getConnection(config.url, config.user, config.password);
        conn.setAutoCommit(false);  // 统一关闭自动提交
        return conn;
    }

    private static boolean zhangSanCanCommit(Connection conn, String xid) throws SQLException {
        // 实际业务检查（示例：检查张三余额）
        PreparedStatement ps = conn.prepareStatement(
                "SELECT balance FROM account WHERE name = '张三'");
        ResultSet rs = ps.executeQuery();
        if (rs.next()) {
            return rs.getDouble(1) >= 1000;  // 余额是否充足
        }
        return false;
    }

    private static boolean lisiCanCommit(Connection conn, String xid) throws SQLException {
        // 实际业务检查（示例：检查李四账户是否存在）
        PreparedStatement ps = conn.prepareStatement(
                "SELECT id FROM account WHERE name ='李四'");
        ResultSet rs = ps.executeQuery();
        if (rs.next()) {
            String id = rs.getString(1);
            if (!StringUtils.isNullOrEmpty(id)) //李四是否存在
            {
             return  true;
            }else {
                return false;
            }

        }
        return false;
    }

    private static boolean participantPreCommit(Connection conn, String xid) throws SQLException {
        // 预提交操作（示例：记录日志）
        PreparedStatement ps = conn.prepareStatement(
                "INSERT INTO transaction_log (xid, operation) VALUES (?, 'FREEZE 1000')");
        ps.setString(1, xid);
        ps.executeUpdate();
        return true;
    }

    private static void updateCoordinatorPhase(Connection conn, String xid, String phase) throws SQLException {
        PreparedStatement ps = conn.prepareStatement(
                "UPDATE transaction_coordinator SET phase = ? WHERE xid = ?");
        ps.setString(1, phase);
        ps.setString(2, xid);
        ps.executeUpdate();
    }

    // ========== 中止事务 ==========
    private static void doAbort(String xid, List<DBConfig> participants) {
        try (Connection coordConn = getConnection(coordinatorConfig)) {
            updateCoordinatorPhase(coordConn, xid, "ABORT");
            for (DBConfig participant : participants) {
                try (Connection conn = getConnection(participant)) {
                    conn.rollback();  // 回滚参与者事务
                }
            }
            System.out.println("事务已回滚！");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}